﻿using System;
using System.Collections.Generic;
using System.Text;

namespace MathTools.Tasks
{
    public class FirstTask : ICalculateData
    {
        #region Types

        private enum MatherialType
        {
            AlCl,
            AlCl2,
            AlCl3,
            GaCl,
            GaCl2,
            GaCl3,
            NH3,
            H2,
            HCl,
            N2,
            AlN,
            GaN,
        };

        #endregion

        #region Fields

        private const double m_g_AlCl = 0;
        private const double m_g_AlCl2 = 0;
        private const double m_g_AlCl3 = 0;
        private const double m_g_H2 = 0;
        private const double m_g_HCl = 10000;
        private const double m_g_N2 = 90000;

        private const double delta = 0.01;
        
        private double m_k1;
        private double m_k2;
        private double m_k3;

        private double m_temperature;

        double[] m_values = new double[ 5 ];
        double[] m_prevValues = null;

        #region StaticDMembers

            static double dAlCl = 0;
            static double dAlCl2 = 0;
            static double dAlCl3 = 0;
            static double dGaCl = 0;
            static double dGaCl2 = 0;
            static double dGaCl3 = 0;
            static double dNH3 = 0;
            static double dH2 = 0;
            static double dHCl = 0;
            static double dN2 = 0;
            static double dAlN = 0;
            static double dGaN = 0;

        #endregion

        #endregion

        public FirstTask()
        {
        }

        public void setKoefs( double k1, double k2, double k3 )
        {
            m_k1 = k1;
            m_k2 = k2;
            m_k3 = k3;
        }

        public void setTemperature( double t )
        {
            m_temperature = t;
        }

        #region ICalculateData Members

        #region Private calculators

        private double getMu( MatherialType type )
        {
            switch( type )
            {
                case MatherialType.AlCl:
                    return 62.4345;

                case MatherialType.AlCl2:
                    return 97.8875;

                case MatherialType.AlCl3:
                    return 133.3405;

                case MatherialType.GaCl:
                    return 105.173;

                case MatherialType.GaCl2:
                    return 140.626;

                case MatherialType.GaCl3:
                    return 176.080;

                case MatherialType.NH3:
                    return 17.031;

                case MatherialType.H2:
                    return 2.016;

                case MatherialType.HCl:
                    return 36.461;

                case MatherialType.N2:
                    return 28.0135;

                case MatherialType.AlN:
                    return 40.988;

                case MatherialType.GaN:
                    return 83.730;
            }

            throw new Exception( "Некоторые значения не определены" );
        }

        private double getSigma( MatherialType type )
        {
            switch( type )
            {
                case MatherialType.AlCl:
                    return 3.58;

                case MatherialType.AlCl2:
                    return 5.3;

                case MatherialType.AlCl3:
                    return 5.13;

                case MatherialType.GaCl:
                    return 3.696;

                case MatherialType.GaCl2:
                    return 4.293;

                case MatherialType.GaCl3:
                    return 5.034;

                case MatherialType.NH3:
                    return 3.0;

                case MatherialType.H2:
                    return 2.93;

                case MatherialType.HCl:
                    return 2.737;

                case MatherialType.N2:
                    return 3.798;
            }

            throw new Exception( "Некоторые значения не определены" );
        }

        private double getEpsilon( MatherialType type )
        {
            switch( type )
            {
                case MatherialType.AlCl:
                    return 932;

                case MatherialType.AlCl2:
                    return 825;

                case MatherialType.AlCl3:
                    return 472;

                case MatherialType.GaCl:
                    return 348.2;

                case MatherialType.GaCl2:
                    return 465;

                case MatherialType.GaCl3:
                    return 548.24;

                case MatherialType.NH3:
                    return 300;

                case MatherialType.H2:
                    return 34.1;

                case MatherialType.HCl:
                    return 167.1;

                case MatherialType.N2:
                    return 71.4;
            }

            throw new Exception( "Некоторые значения не определены" );
        }

        private double getD( MatherialType type)
        {
            double t32 = Math.Sqrt( m_temperature * m_temperature * m_temperature );

            double muH2 = getMu( MatherialType.H2 );
            double mu = getMu( type );
            double avrMu = Math.Sqrt( 2 * muH2 * mu / ( muH2 + mu ) );

            double epsilon = Math.Sqrt( getEpsilon( type ) * getEpsilon(MatherialType.H2 ) );;

            double x = m_temperature / epsilon;

            return
                2.628e-8 * 2 * t32 * epsilon /
                ( ( getSigma( type ) + getSigma( MatherialType.H2 ) ) * 1.074 * avrMu * Math.Pow( x, -0.1604 ) );
        }

        private double getDFast( MatherialType type )
        {

            switch( type )
            {
                case MatherialType.AlCl:
                    if( dAlCl == 0 )
                        dAlCl = getD( MatherialType.AlCl );
                    return dAlCl;

                case MatherialType.AlCl2:
                    if( dAlCl2 == 0 )
                        dAlCl2 = getD( MatherialType.AlCl2 );
                    return dAlCl2;

                case MatherialType.AlCl3:
                    if( dAlCl3 == 0 )
                        dAlCl3 = getD( MatherialType.AlCl3 );
                    return dAlCl3;

                case MatherialType.GaCl:
                    if( dGaCl == 0 )
                        dGaCl = getD( MatherialType.GaCl );
                    return dGaCl;

                case MatherialType.GaCl2:
                    if( dGaCl2 == 0 )
                        dGaCl2 = getD( MatherialType.GaCl2 );
                    return dGaCl2;

                case MatherialType.GaCl3:
                    if( dGaCl3 == 0 )
                        dGaCl3 = getD( MatherialType.GaCl3 );
                    return dGaCl3;

                case MatherialType.NH3:
                    if( dNH3 == 0 )
                        dNH3 = getD( MatherialType.NH3 );
                    return dNH3;

                case MatherialType.H2:
                    if( dH2 == 0 )
                        dH2 = getD( MatherialType.H2 );
                    return dH2;

                case MatherialType.HCl:
                    if( dHCl == 0 )
                        dHCl = getD( MatherialType.HCl );
                    return dHCl;

                case MatherialType.N2:
                    if( dN2 == 0 )
                        dN2 = getD( MatherialType.N2 );
                    return dN2;

                case MatherialType.AlN:
                    if( dAlN == 0 )
                        dAlN = getD( MatherialType.AlN );
                    return dAlN;

                case MatherialType.GaN:
                    if( dGaN == 0 )
                        dGaN = getD( MatherialType.GaN );
                    return dGaN;
            }

            throw new Exception( "Некоторые значения не определены" );
        }

        #endregion

        public uint Dimension
        {
            get
            {
                return 5;
            }
        }

        public double Fitness
        {
            get
            {
                if( m_prevValues == null )
                    return 1000;

                double result = 0;

                for( int i = 0; i < 5; i++ )
                {
                    result += Math.Abs( m_values[ i ] - m_prevValues[ i ] );
                }

                return result;
            }
        }

        public double[] nextVector
        {
            get
            {
                double[] newValues = new double[ 5 ];

                newValues[ 0 ] = m_values[ 3 ] / m_values[ 4 ] * Math.Sqrt( 1 / m_k1 );
                newValues[ 1 ] = m_values[ 3 ] / m_values[ 4 ] * Math.Sqrt( 1 / m_k2 );
                newValues[ 2 ] = m_values[ 3 ] / m_values[ 4 ] * Math.Sqrt( 1 / m_k3 );

                newValues[ 3 ] = m_g_HCl - 2 * m_values[ 4 ] * getDFast( MatherialType.H2 ) / getDFast( MatherialType.HCl );
                newValues[ 4 ] = ( getDFast( MatherialType.HCl ) * ( m_g_HCl - m_values[ 3 ] ) ) / 2 / getDFast( MatherialType.H2 );

                m_prevValues = m_values;

                m_values = newValues;

                return m_values;
            }
        }

        #endregion
    }
}
